// Protocol.hpp
#pragma once
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <unordered_map>
#include <algorithm>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/sendfile.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include "Util.hpp"
#include "Log.hpp"

// class Connect
// {
// public:
//     int sock;
//     std::string ip;
//     uint16_t port;
// };

#define SEP ": "
#define WEB_ROOT "wwwroot"
#define HOME_PAGE "index.html"
#define HTTP_VERSION "HTTP/1.0"
#define LINE_END "\r\n"

#define OK 200
#define BAD_REQUEST 400
#define NOT_FOUND 404
#define SERVER_ERROR 500

static std::string Code2Desc(int code)
{
    switch (code)
    {
    case 200:
        return "OK";
    case 400:
        return "Bad Request";
    case 404:
        return "Not Found";
    case 500:
        return "Server Error";
    default:
        return "Other error";
    }
}

class Suffix2Desc
{
    static std::unordered_map<std::string, std::string> suffix2desc;

public:
    std::string operator()(const std::string &suffix)
    {
        auto iter = suffix2desc.find(suffix);
        if (iter == suffix2desc.end())
        {
            return "text/html";
        }
        return iter->second;
    }
};
std::unordered_map<std::string, std::string> Suffix2Desc::suffix2desc = {
    {".html", "text/html"},
    {".css", "text/css"},
    {".js", "application/javascript"},
    {"jpg", "image/jpeg"},
    {"jpeg", "image/jpeg"},
    {".xml", "application/xml"}};

class HttpRequest
{
public:
    std::string request_line;
    std::vector<std::string> request_header;
    std::string blank;
    std::string request_body;

    // 解析完毕之后的结果
    std::string method;
    std::string uri; // path?args
    std::string version;

    std::unordered_map<std::string, std::string> header_kv;
    size_t content_length;

    std::string path;         // 请求的资源完整路径
    std::string suffix;       // 请求的资源的后缀
    std::string query_string; // 参数

    bool cgi;

public:
    HttpRequest() : content_length(0), cgi(false), suffix(".html") {}
    ~HttpRequest() {}
};

class HttpResponse
{
public:
    std::string status_line;
    std::vector<std::string> response_header;
    std::string blank;
    std::string response_body;

    int status_code; // 状态码

    int fd;   // 响应的网页文件的fd
    int size; // 响应的网页文件的大小

    bool flag; // 表示构建响应是否成功
public:
    HttpResponse() : blank(LINE_END), status_code(OK), fd(-1), flag(true) {}
    ~HttpResponse() {}
};

// 读取请求、分析请求、构建响应
// IO通信
class EndPoint
{
public:
    EndPoint(int sock)
        : _sock(sock), _stop(false)
    {
    }
    ~EndPoint()
    {
        close(_sock);
    }
    bool RecvHttpRequest()
    {
        RecvHttpRequestLine(); // 读取请求行
        if (_stop)
            return false;        // _stop == true表示recv error
        RecvHttpRequestHeader(); // 读取请求报头
        if (_stop)
            return false;         // _stop == true表示recv error
        ParseHttpRequestLine();   // 解析请求行
        ParseHttpRequestHeader(); // 解析请求报头
        RecvHttpRequestBody();    // 读取正文
        if (_stop)
            return false; // _stop == true表示recv error

        return true;
    }

    void BuildHttpResponse()
    {
        // 请求已经全部读完，可以直接构建响应了
        auto &code = _http_response.status_code;
        if (_http_request.method != "GET" && _http_request.method != "POST")
        {
            // 非法请求
            LOG(WARNING, "method is not right");
            code = BAD_REQUEST;
            ProcessNonCgi();
            return;
        }

        if (_http_request.method == "GET")
        {
            size_t pos = _http_request.uri.find('?');
            if (pos != std::string::npos)
            {
                Util::CutString(_http_request.uri, _http_request.path, _http_request.query_string, "?");
                _http_request.cgi = true;
            }
            else
            {
                _http_request.path = _http_request.uri;
            }
        }
        else if (_http_request.method == "POST")
        {
            _http_request.path = _http_request.uri;
            _http_request.cgi = true;
        }
        else
        {
            // do nothing
        }

        std::string tmp_path = _http_request.path;
        _http_request.path = WEB_ROOT;
        _http_request.path += tmp_path;
        if (_http_request.path[_http_request.path.size() - 1] == '/')
        {
            _http_request.path += HOME_PAGE;
        }

        struct stat st;
        if (stat(_http_request.path.c_str(), &st) == 0)
        {
            // 说明资源是存在的
            if (S_ISDIR(st.st_mode))
            {
                // 说明请求的资源是一个目录，且不是以/结尾，不被允许，需要做一下处理
                _http_request.path += '/';
                _http_request.path += HOME_PAGE;
                // 文件从一个目录变成了index.html，需要把属性st更新一下
                stat(_http_request.path.c_str(), &st);
            }
            if ((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH))
            {
                // 说明请求的是一个可执行程序，需要特殊处理
                _http_request.cgi = true;
            }
            _http_response.size = st.st_size; // 保存文件大小
        }
        else
        {
            // 资源不存在
            LOG(WARNING, _http_request.path + " Not Found");
            code = NOT_FOUND;
            ProcessNonCgi();
            return;
        }

        // 走到这里，请求的资源一定存在，且路径已经被处理成了完整路径
        LOG(INFO, _http_request.path.c_str());

        if (_http_request.cgi)
        {
            // GET方法带参 or POST方法
            ProcessCgi(); // 执行目标程序，拿到结果放到_http_response.response_body中
        }

        // 走到这里，请求的处理已全部完成，只需最后构建响应字段
        // 不是CGI，最后返回静态网页，如果是CGI，执行完还是要返回静态网页
        // 不管请求正确还是错误，只要是打开网页文件，都在这里处理，只需要打开文件，拿到fd即可
        ProcessNonCgi();
    }
    void SendHttpResponse()
    {
        if (_http_response.flag == false)
            return;
        // std::cout << "^^^^^^^^^^^^^^^^^^^^^^" << std::endl;
        send(_sock, _http_response.status_line.c_str(), _http_response.status_line.size(), 0);
        for (auto &iter : _http_response.response_header)
        {
            send(_sock, iter.c_str(), iter.size(), 0);
        }
        send(_sock, _http_response.blank.c_str(), _http_response.blank.size(), 0);

        // 发送正文ssssss
        // _http_response.response_body
        if (_http_request.cgi)
        {
            // std::cout << _http_response.response_body << std::endl;
            const char *start = _http_response.response_body.c_str();
            size_t length = _http_response.response_body.size();
            size_t total = 0;
            size_t size = 0;
            while (total < length && (size = send(_sock, start + total, length - total, 0)) > 0)
            {
                total += size;
            }
        }
        else // _http_response.fd
        {
            // std::cout << _http_response.fd << ", _sock: " << _sock << std::endl;
            sendfile(_sock, _http_response.fd, nullptr, _http_response.size);
            // sendfile(1, _http_response.fd, nullptr, _http_response.size);
            close(_http_response.fd);
            // std::cout << "^^^^^^^^^^^^^^^^^^^^^^" << std::endl;
        }
        // std::cout << "^^^^^^^^^^^^^^^^^^^^^^" << std::endl;
    }

private:
    void RecvHttpRequestLine()
    {
        auto &line = _http_request.request_line;
        if (Util::ReadLine(_sock, line) > 0)
        {
            line.resize(line.size() - 1);
            LOG(INFO, line);
        }
        else
        {
            _stop = true;
        }
    }
    void RecvHttpRequestHeader()
    {
        std::string line;
        while (1)
        {
            line.clear();
            if (Util::ReadLine(_sock, line) <= 0)
            {
                _stop = true;
                break;
            }
            if (line == "\n")
            {
                // _http_request.blank = line;
                break;
            }
            line.resize(line.size() - 1);
            _http_request.request_header.push_back(line);
            // LOG(INFO, line);
        }
    }
    void ParseHttpRequestLine()
    {
        auto &line = _http_request.request_line;
        std::stringstream ss(line);
        ss >> _http_request.method >> _http_request.uri >> _http_request.version;

        auto &method = _http_request.method;
        std::transform(method.begin(), method.end(), method.begin(), ::toupper);
    }
    void ParseHttpRequestHeader()
    {
        std::string key;
        std::string value;
        for (auto &iter : _http_request.request_header)
        {
            if (Util::CutString(iter, key, value, SEP))
            {
                _http_request.header_kv.insert({key, value});
            }
        }
    }
    bool IsNeedRecvHttpRequestBody()
    {
        auto &method = _http_request.method;
        if (method == "POST")
        {
            auto &header_kv = _http_request.header_kv;
            auto iter = header_kv.find("Content-Length");
            if (iter != header_kv.end())
            {
                _http_request.content_length = std::stoi(iter->second);
                return true;
            }
        }
        return false;
    }
    void RecvHttpRequestBody()
    {
        if (IsNeedRecvHttpRequestBody())
        {
            int content_length = _http_request.content_length;
            auto &body = _http_request.request_body;

            char ch = 0;
            while (content_length)
            {
                ssize_t s = recv(_sock, &ch, 1, 0);
                if (s > 0)
                {
                    body += ch;
                    --content_length;
                }
                else
                {
                    _stop = true;
                    break;
                }
            }
        }
    }
    void ProcessCgi()
    {
        int &code = _http_response.status_code;
        auto &bin = _http_request.path;                  // 要让子进程执行的目标程序，一定存在
        auto &query_string = _http_request.query_string; // GET方法的参数
        auto &body_text = _http_request.request_body;    // POST方法的参数
        auto &method = _http_request.method;
        int content_length = _http_request.content_length;  // 请求正文的长度
        auto &response_body = _http_response.response_body; // 响应的正文

        // 进程间双向通信，需要创建两个管道
        // in和out是指站在父进程角度
        int input[2];
        int output[2];
        if (pipe(input) < 0)
        {
            // 创建失败
            LOG(ERROR, "pipe input error");
            code = SERVER_ERROR;
            return;
        }
        if (pipe(output) < 0)
        {
            // 创建失败
            LOG(ERROR, "pipe output error");
            close(input[0]);
            close(input[1]);
            code = SERVER_ERROR;
            return;
        }
        // 执行到这里的是新线程，但是只有一个进程，就是httpserver，要执行程序替换需要创建子进程
        pid_t pid = fork();
        if (pid == 0)
        {
            // 子进程
            close(input[0]);
            close(output[1]);

            // 添加环境变量
            std::string method_env = "METHOD=";
            method_env += method;
            putenv((char *)method_env.c_str());
            if (method == "GET")
            {
                std::string query_string_env = "QUERY_STRING=";
                query_string_env += query_string;
                putenv((char *)query_string_env.c_str());
            }
            else if (method == "POST")
            {
                std::string content_length_env = "CONTENT_LENGTH=";
                content_length_env += std::to_string(content_length);
                putenv((char *)content_length_env.c_str());
            }
            else
            {
                // do nothing
            }

            // 重定向
            // 站在子进程角度
            // input[1]：写出 -> 1
            // output[0]：读入 -> 0
            dup2(output[0], 0);
            dup2(input[1], 1);

            // 替换之前做了重定向，替换成功之后，目标程序只需要从0读，向1写即可
            execl(bin.c_str(), bin.c_str(), nullptr);
            exit(5); // 如果替换成功，就不会走到这里，如果走到这里，子进程也就不要再继续往后走了
        }
        else if (pid < 0)
        {
            // 创建子进程失败
            LOG(ERROR, "fork error");
            close(input[0]);
            close(input[1]);
            close(output[0]);
            close(output[1]);
            code = SERVER_ERROR;
            return;
        }
        else
        {
            // 父进程，新线程
            close(input[1]);
            close(output[0]);

            // 向管道写入数据
            if (method == "POST")
            {
                // 正文数据有可能很多，管道大小有限，要小心一次写不完的情况
                const char *start = body_text.c_str();
                size_t total = 0;
                size_t size = 0;
                while (total < content_length && (size = write(output[1], start + total, content_length - total)) > 0)
                {
                    total += size;
                }
            }

            // 从管道读取数据
            char ch = 0;
            while (read(input[0], &ch, 1) > 0)
            {
                response_body += ch;
            }

            int status = 0;
            pid_t ret = waitpid(pid, &status, 0); // 阻塞等待子进程
            if (ret == pid)
            {
                if (!WIFEXITED(status))
                {
                    // 子进程是接收到信号退出的
                    LOG(ERROR, "子进程收到信号");
                    code = SERVER_ERROR;
                }
                if (WEXITSTATUS(status) != 0) // 提取退出码
                {
                    // 我们假设服务器是不出错的，所以如果退出码不为0，一定是客户端传参有问题
                    LOG(ERROR, "子进程错误码被设置");
                    code = BAD_REQUEST;
                }
            }
            else
            {
                code = SERVER_ERROR;
            }

            close(input[0]);
            close(output[1]);
        }
        return;
    }
    void ProcessNonCgi()
    {
        LOG(INFO, "begin make response");
        int &code = _http_response.status_code;
        std::cout << "状态码: " << code << std::endl;

        // 构建状态行
        _http_response.status_line = HTTP_VERSION;
        _http_response.status_line += " ";
        _http_response.status_line += std::to_string(code);
        _http_response.status_line += " ";
        _http_response.status_line += Code2Desc(code);
        _http_response.status_line += LINE_END;

        if (code == OK)
        {
            if (_http_request.cgi)
            {
                _http_response.size = _http_response.response_body.size();
                // std::cout << _http_response.response_body << std::endl;
            }
            else
            {
                // 我们把后缀提取出来，调用rfind从后往前找第一个.，它后面就是文件后缀
                size_t found = _http_request.path.rfind('.');
                if (found == std::string::npos)
                    _http_request.suffix = ".html";
                else
                    _http_request.suffix = _http_request.path.substr(found);
            }
        }
        else
        {
            _http_request.cgi = false; // 如果状态码不是OK，就按返回网页文件处理，所以cgi设置成false
            _http_request.path = WEB_ROOT;
            _http_request.path += "/statusfile/";
            _http_request.path += std::to_string(code);
            _http_request.path += ".html";
            std::cout << "path: " << _http_request.path << std::endl;
            struct stat st;
            if (stat(_http_request.path.c_str(), &st) == 0)
            {
                _http_response.size = st.st_size;
            }
            else
            {
                _http_response.flag = false;
                return;
            }
        }
        // std::cout << "cgi: " << _http_request.cgi << std::endl;
        if (!_http_request.cgi)
        {
            _http_response.fd = open(_http_request.path.c_str(), O_RDONLY);
            // std::cout << "fd: " << _http_response.fd << std::endl;
            if (_http_response.fd < 0)
            {
                _http_response.flag = false;
                return;
            }
        }

        std::string header_line = "Content-Length: ";
        header_line += std::to_string(_http_response.size);
        header_line += LINE_END;
        _http_response.response_header.push_back(header_line);

        header_line = "Content-Type: ";
        header_line += Suffix2Desc()(_http_request.suffix);
        header_line += LINE_END;
        _http_response.response_header.push_back(header_line);
        LOG(INFO, "make response end");
    }

private:
    int _sock;
    HttpRequest _http_request;
    HttpResponse _http_response;
    bool _stop; // 是否要退出
};

// #define DEBUG 1

class CallBack
{
public:
    void operator()(int sock)
    {
        HandlerRequest(sock);
    }
    void HandlerRequest(int sock)
    {
        LOG(INFO, "Handler request begin");
#ifdef DEBUG
        char buffer[4096];
        recv(sock, buffer, sizeof(buffer), 0);
        std::cout << buffer << std::endl;
#else
        EndPoint *ep = new EndPoint(sock);
        if (ep->RecvHttpRequest())
        {
            LOG(INFO, "Recv no error, begin build and send response.");
            ep->BuildHttpResponse();
            ep->SendHttpResponse();
        }
        else
        {
            LOG(WARNING, "Recv error, close connection");
        }
        delete ep;
#endif
        LOG(INFO, "Handler request end");
    }
};

// class Entrance
// {
// public:
//     static void *HandlerRequest(void *args)
//     {
//         LOG(INFO, "Handler request begin");
//         Connect *conn = static_cast<Connect *>(args);
//         std::cout << "get a new link: " << conn->sock << std::endl;
// #ifdef DEBUG
//         char buffer[4096];
//         recv(conn->sock, buffer, sizeof(buffer), 0);
//         std::cout << buffer << std::endl;
// #else
//         EndPoint *ep = new EndPoint(conn->sock);
//         if (ep->RecvHttpRequest())
//         {
//             LOG(INFO, "Recv no error, begin build and send response.");
//             ep->BuildHttpResponse();
//             ep->SendHttpResponse();
//         }
//         else
//         {
//             LOG(WARNING, "Recv error, close connection");
//         }
//         delete ep;
// #endif
//         delete conn;
//         LOG(INFO, "Handler request end");
//         return nullptr;
//     }
// };